<template>
  <PageWrapper>
    <!-- 笔记 -->
    <CollapseContainer class="mt-5" title="文档">
      <markdown-viewer :value="getDoc()" />
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="响应式-对象类型">
      <ol>
        <li>姓名:{{ person.name }}<Button @click="changeName">修改姓名</Button></li>
        <li>年龄:{{ person.age }}<Button @click="changeAge">修改年龄</Button></li>
        <li>对象:{{ person }}<Button @click="changePerson">修改对象</Button></li>
      </ol>
      <template #action>
        <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('对象类型', getObject())" />
      </template>
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="响应式-数组类型">
      <ol>
        <li>
          字符串数组:{{ JSON.stringify(strArray) }}
          <Button @click="addstr">添加</Button>
          <Button @click="deleteStr">删除</Button>
          <Button @click="clearStr">清空</Button>
        </li>
        <li>
          对象数组:{{ personArray }}
          <Button @click="addPerson">添加</Button>
          <Button @click="deletePerson">删除</Button>
          <Button @click="clearPerson">清空</Button>
        </li>
      </ol>
      <template #action>
        <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('数组类型', getArray())" />
      </template>
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="响应式-响应失效">
      <ol>
        <li>person.name:{{ person.name }}<Button @click="changeName">修改</Button></li>
        <li>同步:nameRef:{{ nameRef }}<Button @click="changeNameRef">修改</Button></li>
        <li>失效:nameGet:{{ nameGet }}<Button @click="changeNameGet">修改</Button></li>
        <li>失效:nameLet:{{ nameLet }}<Button @click="changeNameLet">修改</Button></li>
        <li>
          person.name 与 nameRef 同步修改。nameGet 和 nameLet 修改后，不具有响应式。 但当原对象 person.name
          进行变动后，会将 nameGet 和 nameLet 的最新的值同步到页面上。
        </li>
      </ol>
      <template #action>
        <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('响应失效', getInvalid())" />
      </template>
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="响应式-深度引用">
      <ol>
        <li>城市:{{ address.city }}<Button @click="changeCity">修改</Button></li>
        <li>坐标x:{{ address.location.x }}<Button @click="changeLocationX">修改</Button></li>
        <li>坐标y:{{ address.location.y }}<Button @click="changeLocationY">修改</Button></li>
        <li>地址:{{ address }}<Button @click="changeAddress">修改</Button></li>
      </ol>
      <template #action>
        <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('深度引用', getDeep())" />
      </template>
    </CollapseContainer>

    <!-- 源码组件: 必须使用 ref -->
    <SourceDrawer ref="sourceDrawer" />
  </PageWrapper>
</template>
<script lang="ts" setup>
  import { CollapseContainer } from '@/components/Container';
  import { MarkdownViewer } from '@/components/Markdown';
  import { PageWrapper } from '@/components/Page';
  import { Button } from '@/components/Button';
  import SourceDrawer from '@/learn/SourceDrawer.vue';
  import { reactive, toRefs } from 'vue';
  import { Icon } from '@/components/Icon';

  // 定义子组件引用: 也可以使用reactive
  let sourceDrawer = reactive<any>({});

  function openSource(title: string, content: string) {
    sourceDrawer.open({ title, content, width: '60%' });
  }

  let person = reactive({
    name: 'zhangsan',
    age: 20,
  });

  function changeName() {
    person.name += '~';
  }
  function changeAge() {
    person.age += 1;
  }
  function changePerson() {
    let newPerson = { name: 'lisi' };

    // 使用 assign 属性，只能修改部分属性。如果需要替换全部属性，则需要将其它属性全部设置为空
    Object.assign(person, newPerson);
  }

  // 数组类型
  let strArray = reactive<string[]>([]);
  let personArray = reactive<any>([]);

  function addstr() {
    strArray.push('a');
  }
  function deleteStr() {
    strArray.pop();
  }
  function clearStr() {
    // ts 数组没有很好的删除方式，需要通过 splice函数操作。
    // 如果是删除某个元素，则先通过findIndex 找到索引，然后执行 splice($idx,1)
    strArray.splice(0, strArray.length);
  }
  function addPerson() {
    personArray.push({ name: 'zhangsan', age: 23 });
  }
  function deletePerson() {
    personArray.shift();
  }
  function clearPerson() {
    personArray.splice(0, personArray.length);
  }

  // 响应失效
  let nameGet = person.name;
  let { name: nameLet } = person;
  let { name: nameRef } = toRefs(person);

  function changeNameGet() {
    nameGet += '-get';
  }
  function changeNameLet() {
    nameLet += '-let';
  }
  function changeNameRef() {
    nameRef.value += '-ref';
  }

  // 深度引用
  let address = reactive({ city: '北京', location: { x: 100, y: 200 } });

  function changeCity() {
    address.city += '~';
  }
  function changeLocationX() {
    address.location.x += 10;
  }
  function changeLocationY() {
    address.location.y += 20;
  }
  function changeAddress() {
    let newCity = { city: '上海', location: { x: 200, y: 400 } };
    Object.assign(address, newCity);
  }
</script>

<script lang="ts">
  function getDoc() {
    return `
### 核心特点
* reactive 只能用于对象类型和数组类型，不能用于基本类型(int、boolean 等)
* reactive 会将原对象封装为 proxy 对象，没有 value 属性
* reactive 不支持对象本身替换，即对象地址替换。替换后，对象将失去响应式。及时重新使用 reactive 包裹，也不行。因为页面组件中绑定的对象地址已替换
* 解构语法相当于赋值，新生成的对象不具有实时响应性，需要通过 toRef 或 toRefs 修饰为响应对象。

### 基本语法
* 引入 reactive 对象: import { reactive } from 'vue';
* 定义响应式对象:   let obj = reactive({});let array = reactive([]);
* ts/js 和模板中使用，都不需要通过.value 属性:  $reactive.value = xxx
* 解构语法: let { name } = toRef(person); let { name, age } = toRefs(person);

`;
  }

  function getObject() {
    return `
\`\`\`html
<template>
  <CollapseContainer class="mt-5" title="响应式-对象类型">
    <ol>
      <li>姓名:{{ person.name }}<Button @click="changeName">修改姓名</Button></li>
      <li>年龄:{{ person.age }}<Button @click="changeAge">修改年龄</Button></li>
      <li>对象:{{ person }}<Button @click="changePerson">修改对象</Button></li>
    </ol>
    <template #action>
      <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('对象引用', getObject())" />
    </template>
  </CollapseContainer>
</template>

\<script lang="ts" setup\>
  import { reactive } from 'vue';

  let person = reactive({
    name: 'zhangsan',
    age: 20,
  });

  function changeName() {
    person.name += '~';
  }
  function changeAge() {
    person.age += 1;
  }
  function changePerson() {
    let newPerson = { name: 'lisi' };

    // 使用 assign 属性，只能修改部分属性。如果需要替换全部属性，则需要将其它属性全部设置为空
    Object.assign(person, newPerson);
  }
\</script\>
\`\`\`
`;
  }
  function getArray() {
    return `
\`\`\`html
<template>
  <CollapseContainer class="mt-5" title="响应式-数组">
    <ol>
      <li>
        字符串数组:{{ JSON.stringify(strArray) }}
        <Button @click="addstr">添加</Button>
        <Button @click="deleteStr">删除</Button>
        <Button @click="clearStr">清空</Button>
      </li>
      <li>
        对象数组:{{ personArray }}
        <Button @click="addPerson">添加</Button>
        <Button @click="deletePerson">删除</Button>
        <Button @click="clearPerson">清空</Button>
      </li>
    </ol>
    <template #action>
      <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('对象引用', getObject())" />
    </template>
  </CollapseContainer>
</template>

\<script lang="ts" setup\>
  import { reactive } from 'vue';

  // 数组类型
  let strArray = reactive<string[]>([]);
  let personArray = reactive<any>([]);

  function addstr() {
    strArray.push('a');
  }
  function deleteStr() {
    strArray.pop();
  }
  function clearStr() {
    // ts 数组没有很好的删除方式，需要通过 splice函数操作。
    // 如果是删除某个元素，则先通过findIndex 找到索引，然后执行 splice($idx,1)
    strArray.splice(0, strArray.length);
  }
  function addPerson() {
    personArray.push({ name: 'zhangsan', age: 23 });
  }
  function deletePerson() {
    personArray.shift();
  }
  function clearPerson() {
    personArray.splice(0, personArray.length);
  }
\</script\>
\`\`\`
`;
  }
  function getInvalid() {
    return `
\`\`\`html
<template>
  <CollapseContainer class="mt-5" title="响应式-响应失效">
    <ol>
      <li>person.name:{{ person.name }}<Button @click="changeName">修改</Button></li>
      <li>同步:nameRef:{{ nameRef }}<Button @click="changeNameRef">修改</Button></li>
      <li>失效:nameGet:{{ nameGet }}<Button @click="changeNameGet">修改</Button></li>
      <li>失效:nameLet:{{ nameLet }}<Button @click="changeNameLet">修改</Button></li>
      <li>
        person.name 与 nameRef 同步修改。nameGet 和 nameLet 修改后，不具有响应式。 但当原对象 person.name
        进行变动后，会将 nameGet 和 nameLet 的最新的值同步到页面上。
      </li>
    </ol>
    <template #action>
      <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('响应失效', getInvalid())" />
    </template>
  </CollapseContainer>
</template>

\<script lang="ts" setup\>
  import { reactive, toRef } from 'vue';

  let person = reactive({
    name: 'zhangsan',
    age: 20,
  });

  // 响应失效
  let nameGet = person.name;
  let { name: nameLet } = person;
  let { name: nameRef } = toRef(person);

  function changeName() {
    person.name += '~';
  }

  function changeNameGet() {
    nameGet += '-get';
  }
  function changeNameLet() {
    nameLet += '-let';
  }
  function changeNameRef() {
    nameRef.value += '-ref';
  }
\</script\>
\`\`\`
`;
  }

  function getDeep() {
    return `
\`\`\`html
<template>
  <CollapseContainer class="mt-5" title="响应式-深度引用">
    <ol>
      <li>城市:{{ address.city }}<Button @click="changeCity">修改</Button></li>
      <li>坐标x:{{ address.location.x }}<Button @click="changeLocationX">修改</Button></li>
      <li>坐标y:{{ address.location.y }}<Button @click="changeLocationY">修改</Button></li>
      <li>地址:{{ address }}<Button @click="changeAddress">修改</Button></li>
    </ol>
    <template #action>
      <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('深度引用', getDeep())" />
    </template>
  </CollapseContainer>
</template>

\<script lang="ts" setup\>
  import { reactive } from 'vue';

  // 深度引用
  let address = reactive({ city: '北京', location: { x: 100, y: 200 } });

  function changeCity() {
    address.city += '~';
  }
  function changeLocationX() {
    address.location.x += 10;
  }
  function changeLocationY() {
    address.location.y += 20;
  }
  function changeAddress() {
    let newCity = { city: '上海', location: { x: 200, y: 400 } };
    Object.assign(address, newCity);
  }
\</script\>
\`\`\`
`;
  }
</script>

<style lang="less" scoped>
  button {
    margin-left: 5px;
  }
</style>
